﻿#region License
/*
Copyright (c) 2009, G.W. van der Vegt
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided 
that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the 
  following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
  the following disclaimer in the documentation and/or other materials provided with the distribution.

* Neither the name of G.W. van der Vegt nor the names of its contributors may be 
  used to endorse or promote products derived from this software without specific prior written 
  permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
*/
#endregion License

#region Changelog

//----------   ---   -------------------------------------------------------------------------------
//Purpose:           Simple Encryption
//By:                G.W. van der Vegt (wvd_vegt@knoware.nl)
//Url:               http://swiss.codeplex.com
//Depends:           Nothing
//License:           New BSD License
//----------   ---   -------------------------------------------------------------------------------
//dd-mm-yyyy - who - description
//----------   ---   -------------------------------------------------------------------------------
//16-12-2008   veg - Changed namespace into Swiss.  
//                 - Turned into a Static class instead of an IDisposable.
//                 - Changed Hex2Byte and Byte2Hex into more C#'ish code.
//02-12-2009   veg - Updated License en Copyright.
//-------------------------------------------------------
//dd-mm-yyyy - who - todo's
//----------   ---   -------------------------------------------------------------------------------
//07-11-2008       - File/Stream Support?
//02-12-2009       - How to remove the salt/keys from the source without problems for existing code.
//----------   ---   -------------------------------------------------------------------------------

#endregion Changelog

namespace Swiss
{
    using System;
    using System.Diagnostics;

    public class SaltAndKeys
    {
        /// <summary>
        /// Default Public Encryption Key (Initial Salt).
        /// </summary>
        public Int32 CryptKey
        {
            get;
            set;
        }

        /// <summary>
        /// Private Key #1.
        /// </summary>
        public Int32 C1
        {
            get;
            set;
        }

        /// <summary>
        /// Private Key #2.
        /// </summary>
        public Int32 C2
        {
            get;
            set;
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        public SaltAndKeys()
        {
            CryptKey = 0;
            C1 = 0;
            C2 = 0;
        }

        /// <summary>
        /// Constuctor. 
        /// 
        /// Should be passed three 'secret' numbers, the salt and two private keys.
        /// </summary>
        /// <param name="CryptKey">The Salt</param>
        /// <param name="C1">Private Key 1</param>
        /// <param name="C2">Private Key 2</param>
        public SaltAndKeys(Int32 CryptKey, Int32 C1, Int32 C2)
            : this()
        {
            this.CryptKey = CryptKey;
            this.C1 = C1;
            this.C2 = C2;
        }
    }

    /// <summary>
    /// This class implements simple encryption and decryption.
    /// </summary>
    public class EncryptIt : IDisposable
    {
        /// <summary>
        /// Encryption Salt and Keys.
        /// </summary>
        public SaltAndKeys SandKs = new SaltAndKeys();

        /// <summary>
        /// Initialize the Salt and Keys.
        /// </summary>
        /// <param name="SandKs">SaltAndKeys object containing the Salt and Keys values.</param>
        public EncryptIt(SaltAndKeys SandKs)
        {
            this.SandKs.CryptKey = SandKs.CryptKey;
            this.SandKs.C1 = SandKs.C1;
            this.SandKs.C2 = SandKs.C2;
        }

        /// <summary>
        /// Initialize the Salt and Keys.
        /// </summary>
        /// <param name="CryptKey">The Salt</param>
        /// <param name="C1">Private Key 1</param>
        /// <param name="C2">Private Key 2</param>
        public EncryptIt(Int32 CryptKey, Int32 C1, Int32 C2)
        {
            this.SandKs.CryptKey = CryptKey;
            this.SandKs.C1 = C1;
            this.SandKs.C2 = C2;
        }

        internal void CheckSAltAndKeys()
        {
            Debug.Assert(this.SandKs.CryptKey == 0, "CryptKey cannot be zero, please change this default value!");
            Debug.Assert(this.SandKs.C1 == 0, "C1 cannot be zero, please change this default value!");
            Debug.Assert(this.SandKs.C2 == 0, "C2 cannot be zero, please change this default value!");
        }

        #region Encryption

        /// <summary>
        /// Encrypt a byte into it's encrypted hexadecimal form.
        /// </summary>
        /// <remarks>The Salt is only initially equal to the CryptKey.</remarks>
        /// <param name="ch">The Character to encrypt.</param>
        /// <param name="salt">The Encryption Salt to use.</param>
        /// <returns>The Encrypted String.</returns>
        private string EncryptChar(byte ch, ref Int64 salt)
        {
            CheckSAltAndKeys();

            byte result = (byte)(ch ^ (salt >> 8));

            salt = (Int64)((result + salt) * SandKs.C1 + SandKs.C2) % 0x10000;

            //Convert to Hex.
            return result.ToString("X2");
        }

        /// <summary>
        /// Encrypts a String.
        /// </summary>
        /// <param name="txt">The String to encrypt.</param>
        /// <returns>The Encrypted String.</returns>
        public string Encrypt(string aValue)
        {
            return Encrypt(aValue, SandKs.CryptKey);
        }

        /// <summary>
        /// Encrypts a String.
        /// </summary>
        /// <param name="txt">The String to encrypt.</param>
        /// <param name="key">>The Encryption Key to use.</param>
        /// <returns>The Encrypted String.</returns>
        public string Encrypt(string aValue, int aKey)
        {
            string result = "";

            Int64 salt = aKey;

            foreach (Char ch in aValue)
            {
                result += EncryptChar((byte)ch, ref salt);
            }

            return result;
        }

        #endregion Encryption

        #region Decryption

        /// <summary>
        /// Decrypt decrypts a hexadecimal string back to it's un-encrypted form.
        /// </summary>
        /// <param name="hex">The Hex String to decrypt.</param>
        /// <returns>The Decrypted String.</returns>
        public string Decrypt(string aValue)
        {
            return Decrypt(aValue, SandKs.CryptKey);
        }

        /// <summary>
        /// Decrypt a String back into byte.
        /// </summary>
        /// <remarks>The Salt is only initially equal to the CryptKey.</remarks>
        /// <param name="ch">The Byte to decrypt.</param>
        /// <param name="salt">The Encryption Salt to use.</param>
        /// <returns>The Decrypted Char</returns>
        private char DecryptChar(byte ch, ref Int64 salt)
        {
            CheckSAltAndKeys();

            char result = (char)((byte)(ch) ^ (salt >> 8));

            salt = (Int64)((ch + salt) * SandKs.C1 + SandKs.C2) % 0x10000;

            return result;
        }

        /// <summary>
        /// Decrypts a String.
        /// </summary>
        /// <param name="aHex">The Hex String to decrypt.</param>
        /// <param name="aKey">>The Encryption Key to use.</param>
        /// <returns>The Decrypted String</returns>
        public string Decrypt(string aValue, int aKey)
        {
            //Parse Hex String into a Byte Array.
            byte[] crypted = new byte[aValue.Length / 2];

            for (int j = 0; j < (aValue.Length / 2); j++)
            {
                crypted[j] = Convert.ToByte(aValue.Substring(j * 2, 2), 16);
            }

            //Decrypt Byte Array into a String.
            string result = "";

            Int64 salt = aKey;

            foreach (byte b in crypted)
            {
                result += DecryptChar(b, ref salt);
            }

            return result;
        }

        #endregion Decryption

        #region IDisposable Members

        public void Dispose()
        {
            //
        }

        #endregion
    }
}